package cn.thornbird.orgsync.service.impl;

import cn.thornbird.orgsync.entity.City;
import cn.thornbird.orgsync.exception.BadRequestException;
import cn.thornbird.orgsync.exception.NotFoundException;
import cn.thornbird.orgsync.model.PageList;
import cn.thornbird.orgsync.model.combined.CityObject;
import cn.thornbird.orgsync.repository.CityRepository;
import cn.thornbird.orgsync.repository.specification.CitySpecification;
import cn.thornbird.orgsync.service.CityService;
import cn.thornbird.orgsync.vo.record.CityRecord;
import cn.thornbird.orgsync.vo.request.CityQueryRequest;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@AllArgsConstructor
@Transactional(rollbackFor = Exception.class)
@Slf4j
public class CityServiceImpl extends BaseService<CityRepository, City> implements CityService {

    private final CityRepository cityRepository;

    @Override
    public CityObject find(String id) throws NotFoundException {
        City city = findFirstById(cityRepository, id);
        return createCityObject(city);
    }

    @Override
    public List<CityObject> findAll() {
        List<CityObject> list = new ArrayList<>();
        Map<String, List<City>> provinceMap = new HashMap<>();
        List<City> provinces = cityRepository.findProvinces();
        List<City> cities = cityRepository.findCities();
        for (City city : cities) {
            String parentId = city.getParentId();
            if (!provinceMap.containsKey(parentId)) {
                provinceMap.put(parentId, new ArrayList<>());
            }
            provinceMap.get(parentId).add(city);
        }
        for (City province : provinces) {
            CityObject cityObject = new CityObject();
            cityObject.setCity(province);
            cityObject.setChildren(new ArrayList<>());
            if (provinceMap.containsKey(province.getId())) {
                cityObject.getChildren().addAll(provinceMap.get(province.getId()));
            }
            list.add(cityObject);
        }
        return list;
    }

    @Override
    public List<City> findCities(String parentId) {
        if (parentId == null) {
            return cityRepository.findProvinces();
        }
        return cityRepository.findCitiesByParentId(parentId);
    }

    @Override
    public PageList<CityObject> query(CityQueryRequest cityQueryRequest, Integer pageNumber, Integer pageSize) {
        Sort sort = Sort.by(Sort.Order.asc("orderNumber"));
        Pageable pageable = PageList.generatePageable(pageNumber, pageSize, sort);
        Page<City> page = cityRepository.findAll(new CitySpecification(cityQueryRequest), pageable);
        PageList<CityObject> pageList = PageList.createEmptyPageList(page);
        List<City> cities = page.getContent();
        Map<String, City> provinceMap = getProvinceMap();
        for (City city : cities) {
            CityObject cityObject = new CityObject();
            cityObject.setCity(city);
            if (provinceMap.containsKey(city.getParentId())) {
                cityObject.setParent(provinceMap.get(city.getParentId()));
            }
            pageList.getList().add(cityObject);
        }
        return pageList;
    }

    @Override
    public CityObject save(CityRecord cityRecord) throws BadRequestException, NotFoundException {
        City city;
        City province = null;
        checkDuplicateForCity(cityRecord);
        if (cityRecord.getParentId() != null) {
            province = findFirstById(cityRepository, cityRecord.getParentId());
        }
        if (cityRecord.getId() == null) {
            city = new City();
            cityRecord.setProperties(city);
            city.generateNewId();
        } else {
            city = findFirstById(cityRepository, cityRecord.getId());
            cityRecord.setProperties(city);
        }
        if (city.getParentId() == null) {
            city.setOrderNumber(city.getCityNumber() * 1000);
            cityRepository.updateOrderNumbers(city.getId(), city.getOrderNumber());
        } else {
            city.setOrderNumber(province.getOrderNumber() + city.getCityNumber());
        }
        cityRepository.save(city);
        return createCityObject(city);
    }

    @Override
    public CityObject delete(String id) throws BadRequestException, NotFoundException {
        City city = findFirstById(cityRepository, id);
        List<Object> ids = cityRepository.findCityIdsByCityId(id);
        if (ids.size() > 0) {
            throw new BadRequestException((city.getParentId() == null ? "城市" : "省份") + "使用中，无法删除");
        }
        List<City> cities = cityRepository.findCitiesByParentId(id);
        if (cities.size() > 0) {
            throw new BadRequestException("省份使用中，无法删除");
        }
        cityRepository.logicDelete(id);
        if (city.getParentId() == null) {
            cityRepository.logicDeleteByParentId(city.getId());
        }
        return createCityObject(city);
    }

    private void checkDuplicateForCity(CityRecord cityRecord) throws BadRequestException {
        City city;
        if (cityRecord.getParentId() == null) {
            city = cityRepository.findFirstByNameIgnoreCaseAndParentIdIsNull(cityRecord.getName());
        } else {
            city = cityRepository.findFirstByParentIdAndNameIgnoreCase(cityRecord.getParentId(), cityRecord.getName());
        }
        if (city != null) {
            String message = cityRecord.getParentId() == null ? "已存在相同名称的记录" : "已存在相同省份及名称的记录";
            if (cityRecord.getId() == null) {
                throw new BadRequestException(message);
            } else if (!cityRecord.getId().equals(city.getId())) {
                throw new BadRequestException(message);
            }
        }
    }

    private CityObject createCityObject(City city) {
        CityObject cityObject = new CityObject();
        City parent = null;
        if (city.getParentId() != null) {
            parent = cityRepository.findFirstById(city.getParentId());
        }
        cityObject.setCity(city);
        cityObject.setParent(parent);
        return cityObject;
    }

    private Map<String, City> getProvinceMap() {
        HashMap<String, City> provinceMap = new HashMap<>();
        List<City> provinces = cityRepository.findProvinces();
        for (City province : provinces) {
            provinceMap.put(province.getId(), province);
        }
        return provinceMap;
    }

}
